package com.xyhuo.leetcode.reactor;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Reactor implements Runnable {

    final Selector selector;

    final ServerSocketChannel serverSocket;

    static final int EVENT_LOOP_POOL_SIZE = 4;

    final EventLoop[] eventLoopPool = new EventLoop[EVENT_LOOP_POOL_SIZE];
    int currentIndex = 0;

    Reactor(int port) throws IOException {
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(port));
        serverSocket.configureBlocking(false);

        // 创建selector对象，为server SocketChannel对象注册OP_ACCEPT事件
        selector = Selector.open();
        SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);

        // 在SelectionKey对象中保存Acceptor对象，这样Acceptor对象可以传递给OP_ACCEPT事件发生时的处理方法
        sk.attach(new Acceptor());

        // 创建EventLoop对象，每个EventLoop对象启动一个线程
        initEventLoopPool();
    }

    void initEventLoopPool() {
        try {
            for (int i = 0; i < EVENT_LOOP_POOL_SIZE; i++) {
                eventLoopPool[i] = new EventLoop();
            }
        } catch (IOException ex) {

        }
    }


    @Override
    public void run() {

        try {
            while (!Thread.interrupted()) {
                selector.select();

                Set<SelectionKey> selected = selector.selectedKeys();
                Iterator<SelectionKey> it = selected.iterator();
                while (it.hasNext()) {
                    SelectionKey sk = (SelectionKey) (it.next());
                    Acceptor acceptor = (Acceptor) sk.attachment();
                    acceptor.accept();
                }
            }
        } catch (IOException ex) {

        }
    }

    class Acceptor {
        public void accept() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null) {
                    new Handler(nextEventLoop().selector, c);
                }
            } catch (IOException ex) {

            }
        }

        EventLoop nextEventLoop() {
            return eventLoopPool[(currentIndex++) % EVENT_LOOP_POOL_SIZE];
        }
    }

}
